Popular Searches
Popular Course Categories
Popular Courses

Mocking APIs, Navigation, and External Dependencies

Mocking APIs, Navigation, and External Dependencies

Testing and Debugging React Native Applications

Mocking APIs, Navigation, and External Dependencies in React Native

Course: React Native Training

Topic: Mocking APIs, Navigation, and External Dependencies

Course Link: React Native Training at JustAcademy

Demo Link: Register for a Course Demo

JustAcademy Website: Visit JustAcademy


1. Introduction to Mocking in React Native

Mocking is a testing technique in which a real function, API, module, or external service is replaced with a controlled implementation. This implementation allows developers to test application behavior without depending on the real external system.

React Native applications commonly interact with REST APIs, navigation libraries, authentication services, local storage, device permissions, analytics tools, payment gateways, and native modules. Testing all these dependencies directly can make automated tests slow, unreliable, or difficult to execute in a development environment.

Mocking helps isolate the code being tested so developers can control inputs, simulate successful responses, reproduce failures, and verify the application's behavior under different conditions.

1.1 Why Mocking Is Important

  • Isolation: Tests focus on the specific function or component being tested.
  • Speed: Tests avoid unnecessary network requests and expensive external operations.
  • Reliability: Tests do not depend on internet connectivity or third-party service availability.
  • Predictability: Developers control the data returned by mocked dependencies.
  • Error testing: Network failures, timeouts, and rejected requests can be reproduced consistently.
  • Cost reduction: Tests avoid consuming paid API quotas or triggering external transactions.
  • Security: Tests can use artificial data rather than real customer information or production credentials.

1.2 What Can Be Mocked?

Dependency Example Purpose of Mocking
REST API Axios or Fetch Simulate server responses.
Navigation React Navigation Test navigation-dependent behavior or isolate a screen.
Local Storage AsyncStorage Control saved preferences and cached data.
Authentication Firebase Auth Simulate login, logout, and authentication errors.
Native Modules Camera or device APIs Avoid requiring native device functionality in unit tests.
Analytics Tracking services Verify that events are sent with expected data.
Timers setTimeout() Control delayed operations and animation timing.
Third-Party SDKs Maps or payment SDKs Test application logic without calling real services.

2. Understanding Jest Mocking

Jest is a JavaScript testing framework that provides built-in mocking functions. It allows developers to create mock functions, replace module implementations, monitor function calls, and control asynchronous results.

2.1 Common Jest Mocking Methods

Method Description
jest.fn() Creates a mock function that records calls and can have a custom implementation.
jest.mock() Mocks a module or replaces its implementation.
jest.spyOn() Creates a spy on an object method and allows its behavior to be replaced.
mockReturnValue() Configures a mock function to return a specific value.
mockResolvedValue() Configures a mock function to return a Promise that resolves with a value.
mockRejectedValue() Configures a mock function to return a rejected Promise.
mockImplementation() Provides a custom implementation for a mock function.
jest.clearAllMocks() Clears recorded mock calls and usage information.
jest.resetAllMocks() Resets mock usage information and mock implementations.
jest.restoreAllMocks() Restores original implementations for supported spies and replaced properties.

2.2 Creating a Simple Mock Function

test('calls a function with the correct message', () => {
  const showMessage = jest.fn();

  showMessage('Login successful');

  expect(showMessage).toHaveBeenCalledTimes(1);
  expect(showMessage).toHaveBeenCalledWith('Login successful');
});

In this example, jest.fn() creates a mock function. The test calls the function and verifies the number of calls and the argument supplied to it.

2.3 Mocking a Function Return Value

test('returns a mocked user role', () => {
  const getRole = jest.fn().mockReturnValue('admin');

  expect(getRole()).toBe('admin');
});

This approach is useful when a component depends on the result of another function and you want to control that result during testing.

3. Mocking APIs in React Native

APIs allow React Native applications to communicate with backend servers. They may provide user profiles, product lists, payment status, messages, or other application data.

Testing directly against a real API introduces several problems:

  • The server may be unavailable.
  • Network requests may be slow.
  • Responses may change between test runs.
  • Authentication credentials may be required.
  • Repeated requests may consume API quotas.
  • Testing specific error conditions may be difficult.

Mocking an API makes it possible to test the application using predictable responses without contacting the real backend.

3.1 Understanding Fetch API Mocking

The Fetch API is a built-in JavaScript interface for making HTTP requests. A test can replace global.fetch with a Jest mock to control the response.

Suppose an application retrieves a list of products from an API.

3.2 Create an API Service

Create a file named productService.js.

export async function fetchProducts() {
  const response = await fetch(
    'https://api.example.com/products'
  );

  if (!response.ok) {
    throw new Error('Failed to fetch products');
  }

  return response.json();
}

This function requests product data, checks whether the HTTP response was successful, and converts the response body into JSON.

3.3 Mock a Successful Fetch Request

Create productService.test.js.

import { fetchProducts } from './productService';

describe('fetchProducts', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });

  test('returns products from the API', async () => {
    const mockProducts = [
      { id: 1, name: 'Phone' },
      { id: 2, name: 'Laptop' }
    ];

    jest.spyOn(global, 'fetch').mockResolvedValue({
      ok: true,
      json: jest.fn().mockResolvedValue(mockProducts)
    });

    const products = await fetchProducts();

    expect(products).toEqual(mockProducts);
    expect(global.fetch).toHaveBeenCalledWith(
      'https://api.example.com/products'
    );
  });
});

The test replaces the global Fetch implementation, provides a controlled response, and verifies the returned data. It does not contact the real API.

Note: This example assumes that global.fetch exists in the Jest environment. If your environment does not provide it, assign a mock to global.fetch and restore or delete that assignment during cleanup.

3.4 Mock an API Error Response

API tests should also verify what happens when the server returns an unsuccessful HTTP status.

import { fetchProducts } from './productService';

describe('fetchProducts error handling', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });

  test('throws an error when the server response is unsuccessful', async () => {
    jest.spyOn(global, 'fetch').mockResolvedValue({
      ok: false,
      status: 500,
      json: jest.fn()
    });

    await expect(fetchProducts()).rejects.toThrow(
      'Failed to fetch products'
    );
  });
});

This test verifies the behavior of the service when the response is unsuccessful. The JSON parser is not needed because the service throws before parsing the response body.

3.5 Mock a Network Failure

A network failure differs from an HTTP error. In a network failure, the Fetch Promise itself may reject before an HTTP response is received.

test('handles a network failure', async () => {
  jest.spyOn(global, 'fetch').mockRejectedValue(
    new Error('Network unavailable')
  );

  await expect(fetchProducts()).rejects.toThrow(
    'Network unavailable'
  );
});

This example checks that the service propagates a network error. If the service catches errors and returns a fallback value, test that behavior instead.

4. Mocking APIs with Axios

Axios is a popular HTTP client used in React Native applications. It provides methods such as get(), post(), put(), and delete().

4.1 Create an Axios Service

Create userService.js.

import axios from 'axios';

export async function fetchUser(userId) {
  const response = await axios.get(
    `https://api.example.com/users/${userId}`
  );

  return response.data;
}

4.2 Mock Axios Using Jest

Create userService.test.js.

import axios from 'axios';
import { fetchUser } from './userService';

jest.mock('axios');

describe('fetchUser', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });

  test('returns the requested user', async () => {
    const mockUser = {
      id: 10,
      name: 'Asha'
    };

    axios.get.mockResolvedValue({
      data: mockUser
    });

    const result = await fetchUser(10);

    expect(axios.get).toHaveBeenCalledWith(
      'https://api.example.com/users/10'
    );

    expect(result).toEqual(mockUser);
  });
});

The Axios module is mocked so the test can control its output. This allows the service function to be tested without making an actual HTTP request.

4.3 Mock an Axios Request Failure

test('propagates an Axios request error', async () => {
  axios.get.mockRejectedValue(
    new Error('Request failed')
  );

  await expect(fetchUser(10)).rejects.toThrow(
    'Request failed'
  );
});

This test checks the service's error propagation. In a larger suite, clear or reset the mock between tests and configure each test's response explicitly.

4.4 Fetch vs. Axios Mocking

Feature Fetch Axios
Availability Built into supported JavaScript environments. Requires the Axios package.
HTTP error behavior Usually resolves for HTTP errors; check response.ok. Rejects by default for unsuccessful status codes outside its accepted range.
Mocking approach Mock global.fetch. Mock the Axios module or its methods.
Response parsing Call methods such as response.json(). Response data is normally available through response.data.

5. Mocking API Calls Inside React Native Components

Components often request data and display a loading indicator, the returned content, or an error message. Component tests should verify these observable states.

5.1 Create a User List Component

Create UserList.js.

import React, { useEffect, useState } from 'react';
import { Text, View } from 'react-native';
import { fetchProducts } from './productService';

export default function UserList() {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  useEffect(() => {
    let active = true;

    fetchProducts()
      .then(data => {
        if (active) {
          setProducts(data);
        }
      })
      .catch(() => {
        if (active) {
          setError('Unable to load products');
        }
      })
      .finally(() => {
        if (active) {
          setLoading(false);
        }
      });

    return () => {
      active = false;
    };
  }, []);

  if (loading) {
    return <Text>Loading products...</Text>;
  }

  if (error) {
    return <Text>{error}</Text>;
  }

  return (
    <View>
      {products.map(product => (
        <Text key={product.id}>{product.name}</Text>
      ))}
    </View>
  );
}

The cleanup flag prevents the component from updating state after it has been unmounted. In production applications, consider using cancellation or request-abort mechanisms where supported and appropriate.

5.2 Mock the Service and Test Successful Rendering

Create UserList.test.js.

import React from 'react';
import { render, screen, waitFor } from '@testing-library/react-native';
import { fetchProducts } from './productService';
import UserList from './UserList';

jest.mock('./productService', () => ({
  fetchProducts: jest.fn()
}));

describe('UserList', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });

  test('displays products returned by the service', async () => {
    fetchProducts.mockResolvedValue([
      { id: 1, name: 'Phone' },
      { id: 2, name: 'Laptop' }
    ]);

    render(<UserList />);

    expect(screen.getByText('Loading products...')).toBeTruthy();

    expect(await screen.findByText('Phone')).toBeTruthy();
    expect(screen.getByText('Laptop')).toBeTruthy();

    await waitFor(() => {
      expect(fetchProducts).toHaveBeenCalledTimes(1);
    });
  });
});

The test mocks the service rather than the entire component. It verifies the loading state and the product information displayed after the Promise resolves.

5.3 Test the API Error State

test('displays an error when the service rejects', async () => {
  fetchProducts.mockRejectedValue(
    new Error('Server unavailable')
  );

  render(<UserList />);

  expect(
    await screen.findByText('Unable to load products')
  ).toBeTruthy();
});

This test verifies that a rejected service request produces the expected error message. Mocking the service keeps the test focused on the component's response to the failure.

6. Mocking React Navigation

React Navigation is commonly used to manage screen transitions, stacks, tabs, and drawers in React Native applications.

Navigation-dependent components may use:

  • navigation.navigate() to navigate to another screen.
  • navigation.goBack() to return to the previous screen.
  • useNavigation() to access the navigation object.
  • useRoute() to access the current route and its parameters.
  • useFocusEffect() to run logic when a screen becomes focused.

When testing navigation, the best approach depends on the test's goal. If you are verifying that a screen transition works, prefer rendering a real navigator. Mock navigation dependencies only when you need to isolate a smaller unit of behavior.

6.1 Example: A Profile Button

Create HomeScreen.js.

import React from 'react';
import { Button, Text, View } from 'react-native';

export default function HomeScreen({ navigation }) {
  return (
    <View>
      <Text>Home Screen</Text>
      <Button
        title="View Profile"
        onPress={() => navigation.navigate('Profile')}
      />
    </View>
  );
}

6.2 Mock the Navigation Prop

For a focused unit test of the screen, a mock navigation object can verify that the correct navigation method is requested.

import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react-native';
import HomeScreen from './HomeScreen';

test('requests navigation to the Profile screen', () => {
  const navigation = {
    navigate: jest.fn()
  };

  render(<HomeScreen navigation={navigation} />);

  fireEvent.press(screen.getByText('View Profile'));

  expect(navigation.navigate).toHaveBeenCalledWith('Profile');
});

This is useful for a unit test, but it does not prove that the real navigator registers the Profile screen or displays it correctly. Test the complete transition separately using a real navigator.

6.3 Mocking useNavigation()

Some components use the useNavigation() hook instead of receiving a navigation prop.

import React from 'react';
import { Button, Text, View } from 'react-native';
import { useNavigation } from '@react-navigation/native';

export default function WelcomeScreen() {
  const navigation = useNavigation();

  return (
    <View>
      <Text>Welcome</Text>
      <Button
        title="Open Settings"
        onPress={() => navigation.navigate('Settings')}
      />
    </View>
  );
}

A focused unit test can mock the hook while retaining the other exports from the navigation package.

import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react-native';
import { useNavigation } from '@react-navigation/native';
import WelcomeScreen from './WelcomeScreen';

jest.mock('@react-navigation/native', () => ({
  ...jest.requireActual('@react-navigation/native'),
  useNavigation: jest.fn()
}));

test('requests navigation to Settings', () => {
  const navigate = jest.fn();

  useNavigation.mockReturnValue({ navigate });

  render(<WelcomeScreen />);

  fireEvent.press(screen.getByText('Open Settings'));

  expect(navigate).toHaveBeenCalledWith('Settings');
});

This pattern selectively mocks useNavigation() while preserving the rest of the module's real exports. Use it for isolated tests rather than as a replacement for all navigation integration tests.

6.4 Mocking useRoute()

The useRoute() hook provides information about the current route, including route parameters. A screen may use those parameters to display user-specific information.

import React from 'react';
import { Text } from 'react-native';
import { useRoute } from '@react-navigation/native';

export default function ProfileScreen() {
  const route = useRoute();

  return (
    <Text>User ID: {route.params.userId}</Text>
  );
}

Test:

import React from 'react';
import { render, screen } from '@testing-library/react-native';
import { useRoute } from '@react-navigation/native';
import ProfileScreen from './ProfileScreen';

jest.mock('@react-navigation/native', () => ({
  ...jest.requireActual('@react-navigation/native'),
  useRoute: jest.fn()
}));

test('displays the route parameter', () => {
  useRoute.mockReturnValue({
    params: { userId: 42 }
  });

  render(<ProfileScreen />);

  expect(screen.getByText('User ID: 42')).toBeTruthy();
});

For a screen that depends on multiple navigation features, a real navigator may provide more realistic coverage than manually mocking every hook.

7. Testing Real Navigation Between Screens

Integration tests can render a real React Navigation navigator and verify that the expected screen becomes visible after a user interaction.

7.1 Example Navigator

Create AppNavigator.js. This example assumes that the appropriate stack navigator dependencies are installed and configured.

import React from 'react';
import { Button, Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

function HomeScreen({ navigation }) {
  return (
    <View>
      <Text>Home Screen</Text>
      <Button
        title="View Profile"
        onPress={() => navigation.navigate('Profile')}
      />
    </View>
  );
}

function ProfileScreen() {
  return <Text>My Profile</Text>;
}

export default function AppNavigator() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Profile" component={ProfileScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

7.2 Test the Navigation Flow

import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react-native';
import AppNavigator from './AppNavigator';

test('displays the Profile screen after the button is pressed', async () => {
  render(<AppNavigator />);

  fireEvent.press(screen.getByText('View Profile'));

  expect(await screen.findByText('My Profile')).toBeTruthy();
});

This test verifies the visible result of the navigation interaction. It checks more than whether a navigation function was called because the actual navigator must perform the transition.

For projects using animated stack or tab navigators, asynchronous interactions, fake timers, and visibility assertions may be needed depending on the library versions and test setup.

7.3 Mocking Navigation vs. Using a Real Navigator

Approach Best Used For Limitation
Mock navigation prop Testing a screen's reaction to a press. Does not verify actual screen transitions.
Mock useNavigation() Isolating a component that uses the navigation hook. Does not verify navigator configuration or routing.
Real navigator Testing screen transitions and navigation integration. Requires appropriate navigation and native-module setup.

8. Mocking AsyncStorage

AsyncStorage is commonly used to store non-sensitive key-value data such as display preferences, onboarding status, and cached application settings.

Tests should avoid depending on previously stored device data. A mock provides predictable results and prevents the test suite from relying on a real storage implementation.

8.1 Create a Storage Service

Create settingsService.js.

import AsyncStorage from '@react-native-async-storage/async-storage';

const SETTINGS_KEY = 'app_theme';

export async function saveTheme(theme) {
  await AsyncStorage.setItem(SETTINGS_KEY, theme);
}

export async function getTheme() {
  return AsyncStorage.getItem(SETTINGS_KEY);
}

8.2 Configure the AsyncStorage Mock

Use the mock provided by the installed AsyncStorage package when supported by your version. For example, a Jest setup file may contain:

import mockAsyncStorage from
  '@react-native-async-storage/async-storage/jest/async-storage-mock';

jest.mock(
  '@react-native-async-storage/async-storage',
  () => mockAsyncStorage
);

Consult the package documentation for the correct mock path and setup instructions for your installed version.

8.3 Test Saving and Retrieving a Setting

import AsyncStorage from '@react-native-async-storage/async-storage';
import { getTheme, saveTheme } from './settingsService';

describe('settingsService', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  test('saves the selected theme', async () => {
    await saveTheme('dark');

    expect(AsyncStorage.setItem).toHaveBeenCalledWith(
      'app_theme',
      'dark'
    );
  });

  test('retrieves the saved theme', async () => {
    AsyncStorage.getItem.mockResolvedValue('dark');

    const theme = await getTheme();

    expect(theme).toBe('dark');
  });
});

The first test verifies the storage call. The second controls the mock's return value and checks the service result.

9. Mocking External Dependencies and Native Modules

React Native projects often depend on native functionality that cannot run normally in a Node-based Jest environment. Examples include camera access, biometrics, device information, maps, notifications, and animation libraries.

When a dependency relies on native code, its official Jest mock or a small custom mock can replace the implementation during tests.

9.1 Mocking a Native UI Component

Suppose an application uses a third-party video player. A unit test that focuses on the surrounding screen may not need the actual video playback engine.

jest.mock('react-native-video', () => 'Video');

This simple mock replaces the imported component with a placeholder component. It is suitable only when the test does not need to verify video playback behavior or specific native functionality.

9.2 Creating a Manual Mock

For a dependency that requires predictable behavior, create a mock module that exposes the methods used by your application.

For example, create src/services/analytics.js.

export function trackEvent(eventName, properties = {}) {
  // Real analytics implementation.
}

In a test file, replace the module:

jest.mock('./analytics', () => ({
  trackEvent: jest.fn()
}));

Now the test can verify that the application sends the expected event without transmitting real analytics data.

9.3 Testing Analytics Calls

import { trackEvent } from './analytics';
import { completePurchase } from './purchase';

jest.mock('./analytics', () => ({
  trackEvent: jest.fn()
}));

test('tracks a successful purchase', async () => {
  await completePurchase({ orderId: 'ORDER-101' });

  expect(trackEvent).toHaveBeenCalledWith(
    'purchase_completed',
    { orderId: 'ORDER-101' }
  );
});

This example assumes that completePurchase() calls trackEvent() with the supplied event name and properties after a successful purchase. Adapt the assertion to the actual implementation.

9.4 Mocking Device Information

If a component depends on a device-information library, mock only the methods required by the test.

jest.mock('react-native-device-info', () => ({
  getVersion: jest.fn(() => '1.0.0'),
  getSystemName: jest.fn(() => 'Android')
}));

This lets the test use fixed values instead of depending on the machine or device executing the test. Check the installed library's API and Jest guidance before applying a custom mock.

10. Mocking Authentication Services

Authentication providers such as Firebase Authentication may manage login, logout, token refresh, and user sessions. Unit tests should not require real credentials or contact a production authentication service.

10.1 Create an Authentication Service

Create authService.js.

export async function login(authClient, email, password) {
  const result = await authClient.signIn(email, password);
  return result.user;
}

Passing the client as a dependency makes it straightforward to substitute a mock in unit tests.

10.2 Test Successful Authentication

import { login } from './authService';

test('returns the authenticated user', async () => {
  const mockUser = {
    id: 'user-101',
    email: '[email protected]'
  };

  const authClient = {
    signIn: jest.fn().mockResolvedValue({
      user: mockUser
    })
  };

  const user = await login(
    authClient,
    '[email protected]',
    'test-password'
  );

  expect(authClient.signIn).toHaveBeenCalledWith(
    '[email protected]',
    'test-password'
  );

  expect(user).toEqual(mockUser);
});

10.3 Test Authentication Failure

test('propagates an authentication error', async () => {
  const authClient = {
    signIn: jest.fn().mockRejectedValue(
      new Error('Invalid credentials')
    )
  };

  await expect(
    login(authClient, '[email protected]', 'wrong-password')
  ).rejects.toThrow('Invalid credentials');
});

This test verifies the service's behavior when authentication fails. Never place real passwords, private keys, or production tokens in test files.

11. Mocking Timers and Delayed Operations

Applications use timers for debouncing search input, delaying messages, retrying requests, and controlling temporary UI states. Real timers can make tests slower and less predictable.

Jest fake timers allow tests to control the passage of time without waiting for the actual delay.

11.1 Example: Delayed Message

export function showMessageLater(callback) {
  setTimeout(() => {
    callback('Operation completed');
  }, 1000);
}

11.2 Test the Timer

import { showMessageLater } from './messageService';

afterEach(() => {
  jest.useRealTimers();
});

test('displays a message after one second', () => {
  jest.useFakeTimers();

  const callback = jest.fn();

  showMessageLater(callback);

  expect(callback).not.toHaveBeenCalled();

  jest.advanceTimersByTime(1000);

  expect(callback).toHaveBeenCalledWith(
    'Operation completed'
  );
});

The test advances the fake clock by one second and checks that the callback runs. When timers interact with React state updates or navigation animations, use the appropriate act() utilities and asynchronous timer APIs where necessary.

12. Mocking External Modules with jest.spyOn()

jest.spyOn() allows a test to monitor calls to an existing object method. It can also replace the method implementation for a particular test.

12.1 Spy on a Method

const calculator = {
  add(a, b) {
    return a + b;
  }
};

test('tracks calls to the add method', () => {
  const spy = jest.spyOn(calculator, 'add');

  expect(calculator.add(2, 3)).toBe(5);

  expect(spy).toHaveBeenCalledWith(2, 3);

  spy.mockRestore();
});

In this example, the original implementation still runs, while the spy records the call.

12.2 Replace the Implementation

test('replaces a method implementation', () => {
  const spy = jest
    .spyOn(calculator, 'add')
    .mockReturnValue(100);

  expect(calculator.add(2, 3)).toBe(100);

  spy.mockRestore();
});

The original method is restored after the test. Prefer explicit cleanup or a well-configured test setup so spies do not affect later tests.

13. Organizing Jest Setup and Mock Files

As the project grows, repeated mock definitions can be moved into setup files or dedicated mock modules. This helps maintain consistency and reduces duplication.

13.1 Example Project Structure

MyReactNativeApp/
├── src/
│   ├── components/
│   │   └── ProductList.js
│   ├── navigation/
│   │   └── AppNavigator.js
│   ├── services/
│   │   ├── productService.js
│   │   ├── authService.js
│   │   └── analytics.js
│   └── utils/
├── __mocks__/
│   └── analytics.js
├── jest/
│   └── setup.js
├── jest.config.js
└── package.json

13.2 Example Jest Configuration

A React Native Community CLI project may use a configuration similar to the following, adapted for its installed versions and dependencies.

module.exports = {
  preset: 'react-native',
  setupFilesAfterEnv: [
    '<rootDir>/jest/setup.js'
  ]
};

Some native-module mocks need to be loaded through setupFiles, while test matchers or other test-environment configuration may belong in setupFilesAfterEnv. Follow the instructions for the relevant dependency and Jest version.

13.3 Run Tests

npm test

Run a particular test file:

npm test -- ProductList.test.js

Run tests in watch mode:

npm test -- --watch

Generate coverage information:

npm test -- --coverage

14. Common Mocking Mistakes and Troubleshooting

Problem Possible Cause Solution
Real API requests still occur The wrong module was mocked or the mock was applied too late. Check the import path and Jest mock placement.
Mock function returns undefined No return value or implementation was configured. Use mockReturnValue() or mockResolvedValue().
Asynchronous test fails unexpectedly The Promise was not awaited or the assertion ran too early. Use await, return the Promise, or use asynchronous queries.
Navigation hook throws an error The component is rendered without a navigation context or hook mock. Render with the required provider or mock the hook for a focused unit test.
Native module error The library depends on native code unavailable in Jest. Configure the library's supported Jest mock or setup instructions.
Tests influence one another Mocks or shared data are not cleaned up. Clear, reset, or restore mocks as appropriate.
Snapshot changes unexpectedly The rendered output changed or a dependency changed its output. Inspect the difference before updating snapshots.
Navigation animation test hangs Animation timers or asynchronous updates have not completed. Use suitable fake timers and await state updates where required.
Mock does not match the real API The mock's response shape differs from the real service contract. Model realistic response and error structures.

14.1 Best Practices for Troubleshooting

  1. Read the complete error message and identify the failing assertion.
  2. Confirm the exact module path being imported by the component or service.
  3. Verify that the mock returns the same shape expected by the application.
  4. Check whether the operation is synchronous or asynchronous.
  5. Ensure that Promises and UI updates are properly awaited.
  6. Check the dependency's official Jest instructions.
  7. Run the affected test alone to identify shared-state problems.
  8. Restore or reset mocks between tests where necessary.

15. Best Practices for Mocking APIs, Navigation, and Dependencies

15.1 Mock at the Appropriate Layer

If the goal is to test an API service, mock the HTTP client. If the goal is to test a component that consumes the service, mock the service. If the goal is to verify a navigation flow, prefer a real navigator.

15.2 Avoid Over-Mocking

Mocking every dependency can make tests pass even when different parts of the application do not work together. Keep unit tests isolated, but include integration tests that exercise real component and navigation behavior.

15.3 Use Realistic Response Data

API mocks should reflect the actual response structure, including nested objects, arrays, identifiers, and error fields. Unrealistic mocks may hide bugs in how application code processes data.

15.4 Test Both Success and Failure

For each important asynchronous operation, test the expected success path and the relevant failure paths. Include empty data, rejected requests, and unexpected responses when they are part of the service contract.

15.5 Keep Tests Independent

Each test should configure its own mock results and avoid relying on another test's execution order.

15.6 Do Not Expose Sensitive Information

Use dummy users, artificial API responses, and test-only credentials. Never store production secrets or real customer data in test fixtures.

15.7 Test User-Visible Outcomes

For UI and navigation integration tests, check what the user sees after an interaction. Visible results generally provide more useful confidence than checking implementation details alone.

16. Practical Project: Product Screen with API and Navigation Mocking

In this project, you will build a simplified product screen that loads data from a service and offers a button to open a product details screen. You will test the API result, loading state, error state, and navigation behavior.

16.1 Create the Product Service

Create productService.js.

export async function getProducts(apiClient) {
  const response = await apiClient.get('/products');
  return response.data;
}

16.2 Write Service Tests

import { getProducts } from './productService';

describe('getProducts', () => {
  test('returns the product list', async () => {
    const apiClient = {
      get: jest.fn().mockResolvedValue({
        data: [
          { id: 1, name: 'Phone' },
          { id: 2, name: 'Laptop' }
        ]
      })
    };

    const products = await getProducts(apiClient);

    expect(apiClient.get).toHaveBeenCalledWith('/products');

    expect(products).toEqual([
      { id: 1, name: 'Phone' },
      { id: 2, name: 'Laptop' }
    ]);
  });
});

16.3 Create a Product Card

Create ProductCard.js.

import React from 'react';
import { Button, Text, View } from 'react-native';

export default function ProductCard({ product, navigation }) {
  return (
    <View>
      <Text>{product.name}</Text>
      <Button
        title="View Details"
        onPress={() =>
          navigation.navigate('ProductDetails', {
            productId: product.id
          })
        }
      />
    </View>
  );
}

16.4 Test the Product Card's Navigation Request

import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react-native';
import ProductCard from './ProductCard';

test('requests navigation with the selected product ID', () => {
  const navigation = {
    navigate: jest.fn()
  };

  render(
    <ProductCard
      product={{ id: 1, name: 'Phone' }}
      navigation={navigation}
    />
  );

  fireEvent.press(screen.getByText('View Details'));

  expect(navigation.navigate).toHaveBeenCalledWith(
    'ProductDetails',
    { productId: 1 }
  );
});

This test verifies the requested route and parameters. In a separate integration test, render the real navigator and confirm that the Product Details screen displays the selected product.

16.5 Project Learning Outcomes

  • Separate API logic from UI components.
  • Mock API clients with predictable responses.
  • Test service success and failure behavior.
  • Verify navigation requests and route parameters.
  • Understand when to mock a dependency and when to use a real implementation.
  • Build a maintainable Jest test suite for React Native features.

17. Practice Exercises

  1. Create a weather service that retrieves temperature data and mock successful and failed responses.
  2. Write an Axios service test that verifies a request URL and returned response data.
  3. Create a login screen that displays a loading message while an authentication request is pending.
  4. Test an authentication failure and confirm that the expected error message appears.
  5. Build a settings screen that loads a theme from AsyncStorage and test both stored and missing values.
  6. Create a screen that uses useRoute() and test how it displays route parameters.
  7. Render a real stack navigator and test navigation from Home to Profile.
  8. Mock an analytics module and verify that a purchase event is tracked after success.
  9. Use Jest fake timers to test a delayed success message.
  10. Identify a native dependency in your project and configure an appropriate test mock.

18. Interview Questions and Answers

Q1. What is mocking in React Native?

Mocking replaces a real dependency with a controlled implementation so application behavior can be tested without requiring the real external system.

Q2. Why do we mock APIs in unit tests?

Mocking APIs makes tests faster and more predictable, avoids real network requests, and allows developers to test success and failure scenarios consistently.

Q3. What is the difference between jest.fn() and jest.mock()?

jest.fn() creates a mock function. jest.mock() replaces or mocks an entire module or its exports.

Q4. What is the difference between mockResolvedValue() and mockRejectedValue()?

mockResolvedValue() simulates a Promise that succeeds, while mockRejectedValue() simulates a Promise that fails.

Q5. How do you mock the Fetch API?

Replace global.fetch with a Jest mock, provide a controlled response, and assert that the application handles the returned data correctly.

Q6. How do you mock Axios?

Mock the Axios module with jest.mock('axios'), configure methods such as axios.get with resolved or rejected values, and verify the results.

Q7. Should React Navigation always be mocked?

No. Mocking can be useful for isolated unit tests, but real navigators are generally preferable when testing screen transitions and navigation integration.

Q8. How do you test useNavigation()?

For an isolated unit test, selectively mock the useNavigation() hook and verify the component's behavior. For a navigation integration test, render the component within a real navigator.

Q9. What is AsyncStorage mocking?

It replaces the storage implementation with a controlled mock so tests can verify storage calls and simulate saved or missing values without depending on real device storage.

Q10. Why are native modules mocked in Jest?

Some native modules require platform-specific code that is not available in a standard Jest environment. Mocking allows the JavaScript code that depends on them to be tested separately.

Q11. What is jest.spyOn()?

jest.spyOn() monitors calls to an existing object method and can optionally replace its implementation during a test.

Q12. What is the difference between clearing and restoring mocks?

Clearing removes recorded usage information. Restoring returns supported spies or replaced properties to their original implementations. Resetting also resets mock implementations.

Q13. How do you test API errors?

Configure the mock to reject a Promise or return an unsuccessful HTTP response, then verify the service or component's documented error behavior.

Q14. What is the best way to test navigation?

Use a real navigator for screen-transition tests and assert that the expected screen becomes visible. Use navigation mocks for focused unit tests when appropriate.

Q15. What is over-mocking?

Over-mocking occurs when too many dependencies are replaced with mocks, potentially hiding integration defects and making tests less representative of real application behavior.

19. Quick Revision Checklist

  • Understand the purpose and benefits of mocking.
  • Know how to use jest.fn(), jest.mock(), and jest.spyOn().
  • Mock Fetch and Axios requests.
  • Test successful API responses, HTTP errors, and network failures.
  • Mock service functions when testing component behavior.
  • Test loading, success, and error UI states.
  • Understand when to mock navigation and when to render a real navigator.
  • Test route parameters and navigation behavior.
  • Mock AsyncStorage and external native modules.
  • Use dummy data instead of production credentials.
  • Control asynchronous operations and timers correctly.
  • Clear, reset, or restore mocks when appropriate.
  • Keep tests focused, realistic, independent, and maintainable.

20. Conclusion

Mocking APIs, navigation, and external dependencies is an essential skill for React Native developers who want to create reliable automated tests. Jest makes it possible to control function results, simulate server responses, test navigation-related behavior, and replace native or third-party modules with predictable implementations.

The most effective testing strategy uses mocks selectively. Test API services independently, verify component behavior with controlled service responses, and use real navigators for important screen-transition workflows. Include success cases, failure cases, and edge conditions so the application can be tested without relying on live services or physical devices for every test.

By applying these techniques, developers can reduce regressions, simplify debugging, improve test reliability, and build more maintainable React Native applications.

Course Resources: Explore React Native Training | Register for a Course Demo | Visit JustAcademy

whatsapp